package pl.edu.mimuw.irs.rcp.persistence.resource;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Unmarshaller;

import pl.edu.mimuw.irs.core.model.design.resource.Resource;
import pl.edu.mimuw.irs.core.model.design.resource.Resources;
import pl.edu.mimuw.irs.core.model.design.resource.Role;
import pl.edu.mimuw.irs.core.model.design.resource.RoleInstance;
import pl.edu.mimuw.irs.rcp.persistence.resource.history.HistoryType;
import pl.edu.mimuw.irs.rcp.persistence.resource.history.ObjectFactory;
import pl.edu.mimuw.irs.rcp.persistence.resource.history.ResourceType;
import pl.edu.mimuw.irs.rcp.persistence.resource.history.RoleType;
import pl.edu.mimuw.irs.rcp.persistence.resource.history.TaskType;
import pl.edu.mimuw.irs.rcp.util.Pair;

public class ResourcesHistory {

	// *** Attributes

	private File[] files;
	public File[] getFiles() {
		if (files == null) files = new File[0];
		return files;
	}
	
	// *** Constructors
	
	public ResourcesHistory(File[] files) {
		this.files = files;
	}
	
	// *** Methods
	
	public void loadTo(Resources resources) {
		Map<String, Resource> map = new LinkedHashMap<String, Resource>();
		for (Resource resource : resources) {
			map.put(resource.getName(), resource);
		}
		
		for (File file : getFiles()) {
			try {
				InputStream stream = new FileInputStream(file);
				loadFromStream(stream, map);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	@SuppressWarnings("unchecked")
	protected void loadFromStream(InputStream stream, Map<String, Resource> map) throws Exception {
		JAXBContext context = JAXBContext.newInstance(ObjectFactory.class);
		Unmarshaller unmarshaller = context.createUnmarshaller();
		Object root = unmarshaller.unmarshal(stream);
		if (root == null) return;// TODO : neat exception
		if (!(root instanceof JAXBElement<?>)) return;
		JAXBElement<HistoryType> historyElement = (JAXBElement<HistoryType>) root;
		HistoryType historyType = historyElement.getValue();
		
		loadHistory(historyType, map);
	}

	private void loadHistory(HistoryType historyType, Map<String, Resource> map) {
		
		for (ResourceType resourceType : historyType.getResource()) {
			/* If resource has not been defined, we omit it */
			Resource resource = map.get(resourceType.getName());
			if (resource == null) continue;
			
			Map<String, Pair<Integer, Integer>> timeMap = new LinkedHashMap<String, Pair<Integer, Integer>>();
			for (TaskType taskType : resourceType.getTask()) {
				/* Pair(timeExpected, timeTaken) */
				for (RoleType roleType : taskType.getRole()) {
					Pair<Integer, Integer> pair;
					if (timeMap.containsKey(roleType.getName())) {
						pair = timeMap.get(roleType.getName());
						pair.setKey(pair.getKey() + taskType.getTimeExpected());
						pair.setValue(pair.getValue() + taskType.getTimeTaken());
					} else {
						pair = new Pair<Integer, Integer>(taskType.getTimeExpected(), taskType.getTimeTaken());
						timeMap.put(roleType.getName(), pair);
					}
				}
			}

			for (Entry<String, Pair<Integer, Integer>> entry : timeMap.entrySet()) {
				Double timeExpected = Double.valueOf(entry.getValue().getKey());
				Double timeTaken = Double.valueOf(entry.getValue().getValue());
				if (timeExpected <= 0 || timeTaken <= 0) continue;
				Role role = new Role(entry.getKey());
				RoleInstance roleInstance = resource.getRolesMap().get(role);
				Double calculatedModifier = timeTaken / timeExpected;
				if (roleInstance == null) {
					roleInstance = new RoleInstance(role, null, calculatedModifier);
					resource.add(roleInstance);
				} else {
					roleInstance.setCalculatedModifier(calculatedModifier);
				}
			}
		}
	}
}
